package com.jason.lucene.example.search;

import org.apache.lucene.analysis.core.SimpleAnalyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.Explanation;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.PrefixQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TermRangeQuery;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.search.TopFieldDocs;
import org.apache.lucene.store.RAMDirectory;
import org.junit.Assert;
import org.junit.Test;

import java.io.IOException;
import java.util.Objects;

/**
 * @author he peng
 * @create 2017/12/4 14:44
 * @see
 */
public class SearchingTest extends BaseSearchingTest {

    /**
     * 简单的 term 查询
     * @throws Exception
     */
    @Test
    public void termSearching() throws Exception {
        IndexReader indexReader = DirectoryReader.open(super.indexDir);
        IndexSearcher indexSearcher = new IndexSearcher(indexReader);
        Term term = new Term("title", "道德经");
        TermQuery termQuery = new TermQuery(term);
        TopFieldDocs topFieldDocs = indexSearcher.search(termQuery, 10, Sort.INDEXORDER);
        for (ScoreDoc scoreDoc : topFieldDocs.scoreDocs) {
            Explanation explain = indexSearcher.explain(termQuery, scoreDoc.doc);
            System.err.println("lucene explain =====> " + explain);
            Document doc = indexSearcher.doc(scoreDoc.doc);
            System.err.println("hit doc =====> " + doc);
        }

        indexReader.close();
        super.indexDir.close();
    }

    /**
     * 范围查询 TODO 没有奏效,还需要实验
     * @throws Exception
     */
    @Test
    public void stringTermRangeSearching() throws Exception {
        TermRangeQuery rangeQuery = TermRangeQuery.newStringRange("readTime", "2017-01-01 00:00:00", "2017-12-31 23:59:59", true, true);
        IndexReader indexReader = DirectoryReader.open(super.indexDir);
        IndexSearcher indexSearcher = new IndexSearcher(indexReader);
        TopDocs topDocs = indexSearcher.search(rangeQuery, 1000);
        for (int i = 0 ; i < topDocs.scoreDocs.length ; i++ ) {
            System.err.println("hit doc =====> " + indexSearcher.doc(topDocs.scoreDocs[i].doc));
        }

        indexReader.close();
        super.indexDir.close();
    }

    /**
     * 组合查询
     * @throws Exception
     */
    @Test
    public void booleanQuery() throws Exception {

        // java OR junit 默认域包含 java 和 junit 中一个或两个的文档
        BooleanQuery booleanQuery1 = new BooleanQuery.Builder()
                .add(new TermQuery(new Term("subject", "java")), BooleanClause.Occur.SHOULD)
                .add(new TermQuery(new Term("subject", "junit")), BooleanClause.Occur.SHOULD)
                .build();

        // +java +junit ; java AND junit 默认域中同时包含 java 和 junit 的文档
        BooleanQuery booleanQuery2 = new BooleanQuery.Builder()
                .add(new TermQuery(new Term("subject", "java")), BooleanClause.Occur.MUST)
                .add(new TermQuery(new Term("subject", "junit")), BooleanClause.Occur.MUST)
                .build();

        // title:extreme -subject:sports ; title:extreme AND NOT subject:sports
        BooleanQuery booleanQuery3 = new BooleanQuery.Builder()
                .add(new TermQuery(new Term("title", "extreme")), BooleanClause.Occur.MUST)
                .add(new TermQuery(new Term("subject", "sports")), BooleanClause.Occur.MUST_NOT)
                .build();

        IndexReader indexReader = DirectoryReader.open(super.indexDir);
        IndexSearcher searcher = new IndexSearcher(indexReader);
        TopDocs topDocs = searcher.search(booleanQuery3, 1000);
        for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
            System.err.println("hit doc =====> " + searcher.doc(scoreDoc.doc));
        }

        indexReader.close();
        super.indexDir.close();
    }

    /**
     * 通过短语搜索
     * @throws Exception
     */
    @Test
    public void phraseQuery() throws Exception {

    }

    /**
     * lucene 查询表达式测试
     * @throws Exception
     */
    @Test
    public void queryParser() throws Exception {
        IndexReader reader = DirectoryReader.open(super.indexDir);
        QueryParser queryParser = new QueryParser(null , new SimpleAnalyzer());
        // java 默认域中包含 java 的文档
        // java OR junit 默认域包含 java 和 junit 中一个或两个的文档
        // +java +junit ; java AND junit 默认域中同时包含 java 和 junit 的文档
        // title:ant title 域中包含 ant 的文档
        // title:extreme -subject:sports ; title:extreme AND NOT subject:sports
        // title 域中包含 extreme 且 subject 域中不包含 sports 的文档
        // (agile OR extreme) AND methodogy 默认域中包含 methodlogy 且包含
        // agile 和 extreme 中的一个或两个的文档
        // title:"junit in action" title 域为 junit in action 的文档
        // title:"junit action" ~5 title 域中 junit 和 action 之间距离小于 5 的文档 TODO 7.1.0 版本不生效
        // subject:java*  subject 域包含由单词 java 开头的项的文档
        // subject:java~  subject 域包含与java相近的项的文档，如 lava
        // lastmodified : [1/1/09 TO 12/31/09] lastmodified 域值在 2009-1-1 和 2009-12-31 之间的文档
        Query query = queryParser.parse("subject:java\\~");
        IndexSearcher indexSearcher = new IndexSearcher(reader);
        TopDocs topDocs = indexSearcher.search(query, 100);
        for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
            System.err.println("hit doc =====> " + indexSearcher.doc(scoreDoc.doc));
        }

        reader.close();
        super.indexDir.close();
    }

    /**
     * 近实时搜索
     * @throws Exception
     */
    @Test
    public void nearRealTimeSearching() throws Exception {
        RAMDirectory dir = new RAMDirectory();
        IndexWriterConfig writerConf = new IndexWriterConfig(new StandardAnalyzer());
        IndexWriter indexWriter = new IndexWriter(dir, writerConf);
        for (int i = 0 ; i < 10 ; i++ ) {
            Document doc = new Document();
            doc.add(new Field("id" , "" + i , TextField.TYPE_STORED));
            doc.add(new Field("text" , "aaa" , TextField.TYPE_STORED));
            System.err.println("indexing doc ===> " + doc);
            indexWriter.addDocument(doc);
        }

        // Open a near real time IndexReader
        IndexReader indexReader = DirectoryReader.open(indexWriter);
        IndexSearcher indexSearcher = new IndexSearcher(indexReader);
        TermQuery termQuery = new TermQuery(new Term("text", "aaa"));
        TopDocs topDocs = indexSearcher.search(termQuery , 1000);
        Assert.assertEquals(10 , topDocs.totalHits);

        indexWriter.deleteDocuments(new Term("id", "1"));
        Assert.assertTrue(indexWriter.hasDeletions());
        indexWriter.commit();

        IndexReader newIndexReader = DirectoryReader.openIfChanged(
                    (DirectoryReader) indexReader , indexWriter , false);

        Assert.assertFalse(Objects.equals(indexReader , newIndexReader));
        indexReader.close();

        indexSearcher = new IndexSearcher(newIndexReader);
        topDocs = indexSearcher.search(termQuery , 1000);
        Assert.assertEquals(9 , topDocs.totalHits);
    }

    /**
     * 前缀查询
     * @throws Exception
     */
    @Test
    public void prefixQuery() throws Exception {
        IndexReader reader = DirectoryReader.open(super.indexDir);
        IndexSearcher indexSearcher = new IndexSearcher(reader);
        QueryParser queryParser = new QueryParser("category" , getAnalyzer());
        Query prefixQuery = queryParser.parse("\\/教育\\/编程\\*");
        TopFieldDocs topFieldDocs = indexSearcher.search(prefixQuery, 1000, Sort.INDEXORDER);
        for (ScoreDoc scoreDoc : topFieldDocs.scoreDocs) {
            Document doc = indexSearcher.doc(scoreDoc.doc);
            System.err.println("hit doc =====> " + doc);
        }

        reader.close();
        super.indexDir.close();
    }
}
